﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Transactions;
using MvcBoost.EntityFramework;

namespace System.Data
{
    public abstract class DbContextService<TContext>: IDisposable where TContext : DbContext
    {
        public DbContextService()
            : this(new LazyContextManager<TContext>())
        {
        }

        public DbContextService(TContext context)
            : this(context, true)
        {
        }
        
        protected DbContextService(TContext context, bool isSharedContext): 
            this( new IsolatedContextManager<TContext>(context, isSharedContext) )
        {                        
        }

        internal DbContextService(IDbContextManager<TContext> contextManager)
        {
            if (contextManager == null) throw new ArgumentNullException("contextManager");
            this.ContextManager = contextManager;
        }

        internal IDbContextManager<TContext> ContextManager { get; private set; }
        
        public TContext Context
        {
            get { return ContextManager.CreateContext(); }
        }
        
        
        
        public void Dispose()
        {
            this.ContextManager.RemoveContext();
            Dispose(true);
        }


        protected virtual void Dispose(bool disposing)
        {
            // Do nothing
        }
        

        /// <summary>
        /// Prepares the Object Context connection for a local NON-PROMOTED transaction 
        // See: http://msdn.microsoft.com/en-us/library/vstudio/bb738698(v=vs.100).aspx
        // ERR: http://stackoverflow.com/questions/7194106/entityconnection-can-only-be-constructed-with-a-closed-dbconnection
        /// </summary>        
        /// <remarks>This operation is required to avoid TransactionScope promotion</remarks>
        public virtual void PrepareForTransaction()
        {
            // Explictly open the connection
            // See: http://msdn.microsoft.com/en-us/library/vstudio/bb738698(v=vs.100).aspx
            // ERR: http://stackoverflow.com/questions/7194106/entityconnection-can-only-be-constructed-with-a-closed-dbconnection

            ((IObjectContextAdapter)Context).ObjectContext.Connection.Open();
        }

        /// <summary>
        /// Closes the Object Context connection manually after having performed all operations in transaction scope
        /// </summary>
        public virtual void CloseAfterTransaction()
        {
            //DataContext.Database.Connection.Close();
            ((IObjectContextAdapter)Context).ObjectContext.Connection.Close();
        }

        public virtual void Transact(Action action, bool saveChanges)
        {
            try
            {
                PrepareForTransaction();

                using (var t = new TransactionScope())
                {
                    // Do the operation
                    action();

                    if (saveChanges) Context.SaveChanges();

                    // Complete Transaction Scope
                    t.Complete();
                }
            }
            catch
            {
                throw;
            }
            finally
            {
                // Make sure connection is closed after operation
                CloseAfterTransaction();
            }
        }

        public virtual void Transact(Action action)
        {
            Transact(action, true);
        }

        protected virtual void MarkAsModified<TEntity>(TEntity entity) where TEntity : class
        {
            Context.Entry<TEntity>(entity).State = EntityState.Modified;
        }
    }
}
